/**
 * AspectSense project
 *
 * Middleware for annotation-based, context-aware and adaptive services for
 * mobile and ubiquitous computing
 *
 * This project is an open-source effort, started at the University of Cyprus
 * [http://www.ucy.ac.cy].
 *
 * More information about the project is available at
 * [http://code.google.com/p/aspectsense]
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package org.aspectsense.example_apps.multipleUI;

import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.VoiceManager;
import com.sun.speech.freetts.FreeTTSSpeakable;
import com.sun.speech.freetts.audio.AudioPlayer;
import org.aspectsense.annotations.Component;
import org.aspectsense.annotations.Service;
import org.aspectsense.annotations.ServiceUtility;
import org.aspectsense.framework.DefaultComponent;

import javax.sound.sampled.AudioFormat;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/**
 * <p/>
 * Date: Jul 13, 2007<br/>
 * Time: 2:36:43 PM
 * </p>
 *
 * @author Nearchos Paspallis [nearchos@cs.ucy.ac.cy]
 */
@Component(
        displayID = "AudioProcessor"
)
@Service(
        serviceType = AudioProcessorService.class,
        canBeDistributed = true
)
@ServiceUtility(
        targetServiceType = AudioProcessorService.class,
        utilityFunction =
                "0.5 * context[context.infrastructure.memory-availability] + "
              + "0.5 * context[context.infrastructure.cpu-availability]"
)
public class AudioProcessor extends DefaultComponent implements AudioProcessorService
{
    private ByteArrayOutputStream byteArrayOutputStream;

    // 16k Voice
    private Voice voice16k;
    private String voice16kName = "kevin16";

    /**
     * Default, empty constructor.
     *
     */
    public AudioProcessor()
    {
        /* empty */
    }

    public void init()
    {
        byteArrayOutputStream = new ByteArrayOutputStream();

        VoiceManager voiceManager = VoiceManager.getInstance();
        voice16k = voiceManager.getVoice(voice16kName);
        voice16k.allocate();
        voice16k.setAudioPlayer(new ByteArrayAudioPlayer(new BufferedOutputStream(byteArrayOutputStream)));
    }

    public void finit()
    {
        voice16k.deallocate();
    }

    public byte [] composeBytesForText(String text)
    {
        byteArrayOutputStream.reset();

        voice16k.allocate();
        voice16k.setAudioPlayer(new ByteArrayAudioPlayer(new BufferedOutputStream(byteArrayOutputStream)));

        voice16k.speak(text);

        try
        {
            byteArrayOutputStream.flush();
        }
        catch(IOException ioe)
        {
            throw new RuntimeException("An exception occurred while transforming text to bytestream!", ioe);
        }

        return byteArrayOutputStream.toByteArray();
    }

    // ---------------------------------------------------------------------- //

    class ByteArrayAudioPlayer implements AudioPlayer
    {
        private DataOutputStream dataOutputStream;

        private AudioFormat audioFormat;
        private int bytesToPlay = 0;
        private int bytesPlayed = 0;
        private boolean firstByteSent = false;

        public ByteArrayAudioPlayer(final BufferedOutputStream bufferedOutputStream)
        {
            dataOutputStream = new DataOutputStream(bufferedOutputStream);
        }

        public void setAudioFormat(AudioFormat audioFormat)
        {
            this.audioFormat = audioFormat;
        }

        public AudioFormat getAudioFormat()
        {
            return audioFormat;
        }

        public void pause() {}

        public void resume() {}

        public void reset() {}

        public void startFirstSampleTimer() {}

        public void cancel() {}

        public void close() {}

        public float getVolume() { return -1; }

        public void setVolume(float v) {}

        public long getTime() { return -1; }

        public void resetTime() {}

        public void showMetrics() {}

        public boolean drain()
        {
            try
            {
                dataOutputStream.flush();
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
            return true;
        }

        public void begin(int size)
        {
            try
            {
                bytesToPlay = size;
                firstByteSent = false;
                dataOutputStream.writeBytes(String.valueOf(size) + "\n");
                dataOutputStream.flush();
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
        }

        public boolean end()
        {
            if (bytesPlayed < bytesToPlay)
            {
                int bytesNotPlayed = bytesToPlay - bytesPlayed;
                write(new byte[bytesNotPlayed], 0, bytesNotPlayed);
            }

            bytesToPlay = 0;
            bytesPlayed = 0;
            return true;
        }

        public boolean write(byte[] bytes)
        {
            return write(bytes, 0, bytes.length);
        }

        public boolean write(byte[] bytes, int offset, int size)
        {
            try
            {
                if (!firstByteSent)
                {
                    firstByteSent = true;
                }

                bytesPlayed += size;
                dataOutputStream.write(bytes, offset, size);
                dataOutputStream.flush();

                return true;
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
                return false;
            }
        }
    }

    // ---------------------------------------------------------------------- //
}